﻿// Copyright (c) 2008, Colin Burn
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// * Neither the name of WTCL nor the names of its contributors may be
// used to endorse or promote products derived from this software
// without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using WTCL.Charts.Axis.Area;
using WTCL.Data.Series;

namespace WTCL.Charts
{
    public abstract class Chart<TSeries, TData, TChart, TChartCanvas> : Grid
        where TSeries : SeriesBase<TData>
        where TChart : Chart<TSeries, TData, TChart, TChartCanvas>
        where TChartCanvas : BaseChartCanvas<TSeries, TData, TChart, TChartCanvas>
    {
        private TChartCanvas chartCanvas;
        internal TChartCanvas ChartCanvas
        {
            get { return this.chartCanvas; }
        }

        private BaseAxis<TChartCanvas> yAxisCanvas;
        internal BaseAxis<TChartCanvas> YAxisCanvas
        {
            get { return this.yAxisCanvas; }
        }

        private BaseAxis<TChartCanvas> xAxisCanvas;
        internal BaseAxis<TChartCanvas> XAxisCanvas
        {
            get { return this.xAxisCanvas; }
        }

        private ObservableCollection<TSeries> series = new ObservableCollection<TSeries>();
        public ObservableCollection<TSeries> Series
        {
            get { return this.series; }
            set
            {
                this.series.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnSeriesChanged);

                if (value == null)
                {
                    this.series = new ObservableCollection<TSeries>();
                }
                else
                {
                    this.series = value;
                }

                this.series.CollectionChanged += new NotifyCollectionChangedEventHandler(OnSeriesChanged);

                this.chartCanvas.HintSeriesChanged();
            }
        }

        private int activeSeries;
        internal int ActiveSeries
        {
            get { return this.activeSeries; }
            set { this.activeSeries = value; }
        }

        private void OnSeriesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (TSeries series in e.OldItems)
                {
                    series.Samples.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnSamplesCollectionChanged);
                }
            }

            if (e.NewItems != null)
            {
                foreach (TSeries series in e.NewItems)
                {
                    series.Samples.CollectionChanged += new NotifyCollectionChangedEventHandler(OnSamplesCollectionChanged);
                }
            }

            this.chartCanvas.HintSeriesChanged();
        }

        private void OnSamplesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            int index = -1;
            for (int i = 0; i < this.series.Count; ++i)
            {
                if (this.series[i].Samples == (ObservableCollection<TData>)sender)
                {
                    index = i;
                    break;
                }
            }

            int sampleIndex = 0;

            if (e.NewStartingIndex >= 0 && e.OldStartingIndex >= 0)
            {
                sampleIndex = Math.Min(e.NewStartingIndex, e.OldStartingIndex);
            }
            else if (e.NewStartingIndex >= 0)
            {
                sampleIndex = e.NewStartingIndex;
            }
            else
            {
                sampleIndex = e.OldStartingIndex;
            }

            this.chartCanvas.HintSeriesChanged(index, sampleIndex);
        }

        /// <summary>
        /// Default constructor. Plese Do not use this. This is only provided 
        /// so that the assembly can be loaded from the XAML file
        /// </summary>
        internal Chart(TChartCanvas chartCanvas, BaseAxis<TChartCanvas> xAxisCanvas, BaseAxis<TChartCanvas> yAxisCanvas)
        {
            ColumnDefinition firstColumn = new ColumnDefinition();
            firstColumn.Width = GridLength.Auto;

            this.ColumnDefinitions.Add(firstColumn);
            this.ColumnDefinitions.Add(new ColumnDefinition());

            RowDefinition lastRow = new RowDefinition();
            lastRow.Height = GridLength.Auto;

            this.RowDefinitions.Add(new RowDefinition());
            this.RowDefinitions.Add(lastRow);

            this.chartCanvas = chartCanvas;
            this.xAxisCanvas = xAxisCanvas;
            this.yAxisCanvas = yAxisCanvas;

            Grid.SetColumn(this.chartCanvas, 1);
            Grid.SetRow(this.chartCanvas, 0);

            this.Children.Add(this.chartCanvas);

            if (this.xAxisCanvas != null)
            {
                this.xAxisCanvas.ChartCanvas = this.chartCanvas;

                Grid.SetColumn(this.xAxisCanvas, 1);
                Grid.SetRow(this.xAxisCanvas, 1);

                this.Children.Add(this.xAxisCanvas);
            }

            if (this.yAxisCanvas != null)
            {
                this.yAxisCanvas.ChartCanvas = this.chartCanvas;

                Grid.SetColumn(this.yAxisCanvas, 0);
                Grid.SetRow(this.yAxisCanvas, 0);

                this.Children.Add(this.yAxisCanvas);
            }

            this.series.CollectionChanged += new NotifyCollectionChangedEventHandler(OnSeriesChanged);
        }
    }
}